#include "idt.h"
#include "kernel.h"
#include "io.h"

#define IDT_LENGTH 255

#define PIC_M_CTRL 0x20
#define PIC_M_DATA 0x21
#define PIC_S_CTRL 0xa0
#define PIC_S_DATA 0xa1
#define PIC_EOI 0x20

extern void clock_handler_entry();

extern void keymap_handler_entry();

extern void rtc_handler_entry();

extern void cpu_broadcast_handler_entry();

extern void time_slice_handler_entry();

extern int64 interrupt_handler_table[0x1f];

static idt_item_t idt_table[IDT_LENGTH] = {0};

idtr_data_t idtr_data;


void send_eoi(int idt_index)
{
    // 如果是主片，只需要发主片就行了，从片则需要发主片和从片
    if (idt_index >= 0x20 && idt_index < 0x28)
    {
        out_byte(PIC_M_CTRL, PIC_EOI);
    } 
    else if (idt_index >= 0x28 && idt_index < 0x30)
    {
        out_byte(PIC_M_CTRL, PIC_EOI);
        out_byte(PIC_S_CTRL, PIC_EOI);
    }
}

void general_interrupt_handler()
{
    printk("error!\n");
    while(1);
}

void install_idt_item(int idx, int64 handler, short selector, char ist, char dpl)
{
    idt_item_t* item = &idt_table[idx];
    item->offset0 = handler & 0xffff;
    item->selector = selector;
    item->ist = ist;
    item->type = 0b1110;
    item->segment = 0;
    item->dpl = dpl;
    item->present = 1;
    item->offset1 = (handler >> 16) & 0xffff;
    item->offset2 = (handler >> 32) & 0xffffffff;
    item->reserved = 0;
}

void idt_init()
{
    int64 handler = (int64) general_interrupt_handler;

    for (size_t i = 0; i < IDT_LENGTH; i++)
    {
        if (i < 0x1f)
        {
            install_idt_item(i, (int64) interrupt_handler_table[i], 0x18, 0, 0);
        }
        else {
            install_idt_item(i, handler, 0x18, 0, 0);
        }
    }

    install_idt_item(0x20, (int64)&clock_handler_entry, 0x18, 0, 0);
    install_idt_item(0x21, (int64)&keymap_handler_entry, 0x18, 0, 0);
    install_idt_item(0x28, (int64)&rtc_handler_entry, 0x18, 0, 0);
    install_idt_item(0xf0, (int64)&cpu_broadcast_handler_entry, 0x18, 0, 0);
    install_idt_item(0xf1, (int64)&time_slice_handler_entry, 0x18, 0, 0);
    
    idtr_data.limit = sizeof(idt_table) - 1;
    idtr_data.base = (int64) &idt_table;

    asm volatile("lidt %0;"::"m"(idtr_data));
}